/// <reference path="libraries/jquery.js" />
/// <reference path="CrmSoapFacade.js" />
/// <reference path="libraries/knockout.js" />
/// <reference path="../../../XrmPageTemplate.js" />

///
/// tuneMultiCheckBox
///
/// authors
///     tuneCRM (tunecrm@gmail.com http://tunemulticheckbox.codeplex.com)
///     
/// version
///     1.0.1 (beta)
///
/// requires 
///     jquery.1.8.2.js
///     knockout.js
///     CrmSoapFacade.js
///
(function ($, jQuery, facade) {
  window.TuneMultiCheckBox = function () {

    var fakeSavingField;
    var labelFieldName;
    var linkedEntitySelectControlField;
    var page = window.parent.Xrm.Page;
    var fakeSavingAttribute;
    
    var formType = {
      FORM_TYPE_CREATE: 1,
      FORM_TYPE_UPDATE: 2,
      FORM_TYPE_READ_ONLY: 3,
      FORM_TYPE_DISABLED: 4,
      FORM_TYPE_QUICK_CREATE: 5,
      FORM_TYPE_BULK_EDIT: 6
    };

    $(document).unbind('keydown').bind('keydown', function (event) {
      if (event.keyCode === 8) {
        event.preventDefault();
      }
    });

    var viewModel = function (columnCount) {
      var self = this;
      self.DataLoaded = ko.observable(false);
      self.CheckboxItemSet = ko.observableArray([]);
      self.CheckboxItemSetByRows = ko.observableArray([]);
      self.ColumnCount = ko.observable((parseInt(columnCount) || 1));
      var enabled = null;
      this.LoadData = function () {
        var dfd = $.Deferred();
        getData().then(
          function (data) {
            var fakeFieldValue = getCurrentFakeFieldValueSet();
            
            var result = [];
            for (var i = 0, max = data.entities.length; i < max; i++) {
              var item = data.entities[i];
              var nameAttr = item.attributes[labelFieldName];
              var checked = item.attributes[linkedEntitySelectControlField] != "undefined" && item.attributes[linkedEntitySelectControlField] != null;
              if (fakeFieldValue) {
                checked = fakeFieldValue.CheckedValues.indexOf(item.id) != -1;
              }
              
              var val = ko.observable(checked);
              val.subscribe(onCheckboxChecked);
              result.push({
                Id: item.id,
                Name: nameAttr == null ? null : nameAttr.value,
                Value: val
              });
            }

            self.CheckboxItemSet = ko.observableArray(result);
            var value = buildStringValue();
            fakeSavingAttribute.setSubmitMode("never");
            fakeSavingAttribute.setValue(value);

            var resultByRows = [],
              row,
              colCount = self.ColumnCount();

            for (var i = 0, m = result.length; i < m; i++) {
              if (i % colCount === 0) {
                if (row) {
                  resultByRows.push(row);
                }

                row = [];
              }

              row.push(result[i]);
            }

            if (row) {
              resultByRows.push(row);
            }

            self.CheckboxItemSetByRows = ko.observableArray(resultByRows);

            self.DataLoaded(true);

            dfd.resolve();
          },
          dfd.reject
        );

        return dfd.promise();
      };
      
      this.GetIsEnabled = function () {
        if (enabled == null) {
          switch (page.ui.getFormType()) {
            case formType.FORM_TYPE_CREATE:
            case formType.FORM_TYPE_UPDATE:
              enabled = true;
              break;
              
            default:
              enabled = false;
          }
        } 

        return enabled;
      };

      var onCheckboxChecked = function () {
        var value = buildStringValue();
        fakeSavingAttribute.setSubmitMode("always");
        fakeSavingAttribute.setValue(value);
      };

      var buildStringValue = function() {
        var checkedSet = [];
        var uncheckedSet = [];
        var arr = self.CheckboxItemSet();
        for (var i = 0, max = arr.length; i < max; i++) {
          if (arr[i].Value()) {
            checkedSet.push(arr[i].Id);
          } else {
            uncheckedSet.push(arr[i].Id);
          }
        }

        return checkedSet.join(';') + '|' + uncheckedSet.join(';');
      };
    };

    function getConfig(entityName, checkboxSetName) {
      var path = "../../../xml/TuneMultiCheckBox/config.xml";
      var dfd = $.Deferred();
      facade.ReadXmlWebResource(path).then(function (xml) {
        var errorMessage =
          "Error! TuneMultiCheckBox is not configured properly. Checkbox Set '" +
            checkboxSetName + "' configuration is not found for '" +
            entityName + "' entity in config.xml.";

        var nodes = $(xml).find("crmEntity[logicalName=" + entityName + "] checkboxSet[name=" + checkboxSetName + "]");
        if (nodes.length == 0) {
          dfd.reject({
            message: errorMessage
          });
          
          return;
        }
        
        var checkboxSetNode = nodes.eq(0);
        var fetchXmlNode = checkboxSetNode.children().first().children().first();
        if (fetchXmlNode.length == 0) {
          dfd.reject({
            message: errorMessage
          });

          return;
        }

        var fetch = facade.XmlToString(fetchXmlNode[0]);
        var result = {
          fakeSavingField: checkboxSetNode.attr("fakeSavingField"),
          relationshipName: checkboxSetNode.attr("relationshipName"),
          labelFieldName: checkboxSetNode.attr("labelFieldName"),
          fetchXml: fetch
        };

        dfd.resolve(result);
      }, 
        dfd.reject
      );

      return dfd.promise();
    }

    function getData() {
      var dfd = $.Deferred();

      var entityName = getWebResourceParam("typename");
      if (!entityName) {
        dfd.reject({ message: 'Error! Entity type is not provided to the checkbox set control.' });
        return dfd.promise();
      }

      var id = decodeURIComponent(getWebResourceParam("id"));

      var dataParams = getWebResourceDataParams();
      if (!dataParams) {
        dfd.reject({ message: 'Error! Data parameters is not provided to the checkbox set control.' });
        return dfd.promise();
      }
      
      var checkboxSetName = dataParams['checkboxSetName'];

      var validParams = checkboxSetName;
      if (!validParams) {
        dfd.reject({ message: 'Error! Data parameters provided to the checkbox set control are invalid.' });
        return dfd.promise();
      }

      getConfig(entityName, checkboxSetName).then(function(config) {
        labelFieldName = $.trim(config.labelFieldName.toLowerCase());
        var relationshipName = $.trim(config.relationshipName.toLocaleLowerCase());
        fakeSavingField = $.trim(config.fakeSavingField.toLowerCase());
        fakeSavingAttribute = page.getAttribute(fakeSavingField);

        var fetchXml = decodeURIComponent(config.fetchXml);

        var entity1 = entityName;
        var entity2;
        var entity1IntersectAttribute;
        var entity2IntersectAttribute;

        if (page.ui.getFormType() == formType.FORM_TYPE_CREATE) {
          facade.FetchAll(fetchXml).then(
            function (data) {
              dfd.resolve(data);
              return;
            },

            dfd.reject
          );

          return;
        } else {
          facade.RetrieveRelationship(relationshipName).then(function (metadata) {
            if (entity1 === metadata.Entity1LogicalName) {
              entity1IntersectAttribute = metadata.Entity1IntersectAttribute;
              entity2 = metadata.Entity2LogicalName;
              entity2IntersectAttribute = metadata.Entity2IntersectAttribute;
            } else if (entity1 === metadata.Entity2LogicalName) {
              entity1IntersectAttribute = metadata.Entity2IntersectAttribute;
              entity2 = metadata.Entity1LogicalName;
              entity2IntersectAttribute = metadata.Entity1IntersectAttribute;
            } else {
              dfd.reject(new Error("Current entity does not contian the relationship specified."));
            }

            var intersectEntityName = metadata.IntersectEntityName;
            var linkedEntityAlias = intersectEntityName + "_checkboxes";
            linkedEntitySelectControlField = linkedEntityAlias + "." + entity1IntersectAttribute;

            var linkedEntity = new facade.FetchLinkedEntityElem(intersectEntityName, linkedEntityAlias, entity2IntersectAttribute, entity2IntersectAttribute, 'outer', false, true,
              [
                new facade.FetchAttributeElem(entity1IntersectAttribute)
              ],

              new facade.FetchFilterElem('and', null, [
                new facade.FetchConditionElem(entity1IntersectAttribute, 'eq', id)
              ]
            ));

            fetchXml = fetchXml.replace("</entity>", (linkedEntity.toString() + "</entity>"));

            facade.FetchAll(fetchXml).then(
              function (data) {
                dfd.resolve(data);
                return;
              },

              dfd.reject
            );
          },
            dfd.reject
          );
        }
      },
        dfd.reject
      );

      return dfd.promise();
    }

    function getCurrentFakeFieldValueSet() {
      var idsString = fakeSavingAttribute.getValue();
      if (idsString == null || idsString == "") {
        return null;
      }
      
      var checkedValues = [];
      var uncheckedValues = [];
      var idsArray = idsString.split("|");
      if (idsArray.length == 2) {
        checkedValues = idsArray[0].split(";");
        uncheckedValues = idsArray[1].split(";");
      } else {
        return null;
      }

      return {
        CheckedValues: checkedValues,
        UncheckedValues: uncheckedValues
      };
    }

    function getWebResourceParam(name) {

      if (location.search != "") {
        var vals = location.search.substr(1).split("&");
        for (var i in vals) {
          vals[i] = vals[i].replace(/\+/g, " ").split("=");
        }
        
        for (var j in vals) {
          if (vals[j][0].toLowerCase() == name) {
            return vals[j][1];
          }
        }
      }
      
      return null;
    }

    function getWebResourceDataParams() {
      var dataParams = getWebResourceParam("data");
      if (dataParams == null) {
        return null;
      }

      var result = {};

      var vals = decodeURIComponent(dataParams).split("&");
      for (var i in vals) {
        var pair = vals[i].replace(/\+/g, " ").split("=");
        result[pair[0]] = pair[1];
      }

      return result;
    }

    return {
      ViewModel: viewModel
    };

  }();
})(window.crmjQuery, window.crmjQuery, window.CrmSoapFacade);